package edu.cmu.cs211.exprtree.ast;

import java.math.BigDecimal;
import java.util.Map;

/* This class evaluates and constant-folds all add expressions
 * e.g. when the operator = '+'
 * There are 2 operands, left and right
 */
public final class AddExpression extends BinaryExpression {
	
	// fields
	Expression l;
	Expression r;
	
	// constructor
	public AddExpression(Expression l, Expression r) 
	{
		super(l, r);
		this.l = l;
		this.r = r;
	}

	/*  AddExpression.evaluate: replaces the variable with the value in hashmap
	 *  and evaluate the expression
	 *  returns a BigDecimal value of the expression
	 */
	public BigDecimal evaluate(Map<String, BigDecimal> variableAssignments) 
	{
		// recurse until the leaf node then start evaluating
		if(l != null || r != null)
		{
			l = new ConstantExpression(l.evaluate(variableAssignments));
			r = new ConstantExpression(r.evaluate(variableAssignments));
		}
		
		String key = null;
		String key2 = null;
		
		//throws an NullPointerException if map is required and is empty
		if(variableAssignments == null && (l.getClass().equals(VariableExpression.class) || r.getClass().equals(VariableExpression.class)))
			throw new NullPointerException();
	
		
		//l + r
		if(l.getClass().equals(VariableExpression.class) && r.getClass().equals(VariableExpression.class))
		{
			if(variableAssignments.containsKey(key = ((VariableExpression)l).getName()) && variableAssignments.containsKey(key2 = ((VariableExpression)r).getName()))
			{
				return variableAssignments.get(key).add(variableAssignments.get(key2));
			}
			else
				throw new IllegalArgumentException();
		}
		
		// l + c2
		if(l.getClass().equals(VariableExpression.class) && r.getClass().equals(ConstantExpression.class))
		{
			if(variableAssignments.containsKey(key = ((VariableExpression)l).getName()))
			{
				return variableAssignments.get(key).add(((ConstantExpression)r).getValue());
			}
			else
				throw new IllegalArgumentException();
		}
		
		// c1 + r
		if(r.getClass().equals(VariableExpression.class) && l.getClass().equals(ConstantExpression.class))
		{
			if(variableAssignments.containsKey(key = ((VariableExpression)r).getName()))
			{
				return variableAssignments.get(key).add(((ConstantExpression)l).getValue());
			}
			else
				throw new IllegalArgumentException();
		}
		
		// c1 + c2
		return ((ConstantExpression)l).getValue().add(((ConstantExpression)r).getValue());
	}

	/**
	 * Constant folding for this node. Uses the following rules:
	 * <p>
	 * (cN = constant expr, eN = any expression)
	 * <p>
	 * <ul>
	 * <li>(c1+c2) ??? constant with value c1+c2
	 * <li>(e1+0) ??? e1
	 * <li>(0+e1) ??? e1
	 * </ul>
	 */
	public Expression cfold() 
	{
		// recurse until the leaf node and start folding
		if(l != null || r != null)
		{
			l = l.cfold();
			r = r.cfold();
		}
		
		// c1 + c2
		if(l.getClass().equals(ConstantExpression.class) && r.getClass().equals(ConstantExpression.class))
			return new ConstantExpression((((ConstantExpression)l).getValue() ).add( ((ConstantExpression)r).getValue() ));	
		
		// 0 + e1
		else if (l.getClass().equals(ConstantExpression.class) && (BigDecimalUtils.isZero(((ConstantExpression)l).getValue())))
			return r;	
		
		// e1 + 0
		else if (r.getClass().equals(ConstantExpression.class) &&(BigDecimalUtils.isZero(((ConstantExpression)r).getValue())))
			return l;

		// e1 + e2
		return new AddExpression(l, r);
	}

	/** @return The symbol for addition, `+' */
	@Override
	public char getOperatorSymbol() 
	{
		return '+';
	}

}
